package org.ala.tiktools.sharddb.db.config;

import org.ala.tiktools.logs.LoggerFactory;
import org.ala.tiktools.sharddb.ConfigPrefixParser;
import org.ala.tiktools.sharddb.Namespace;
import org.slf4j.Logger;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;

import java.util.*;
import java.util.function.Function;


/**
 * 分库配置信息解析器
 *
 * @author ala
 * @date 2024-12-27 10:45
 */
public class ShardDataSourceConfigInfoParser {


    /**
     *  配置项
     */
    protected Map<String, ShardDataSourceConfigInfo> mapper;
    /**
     *  默认配置项
     */
    protected ShardDataSourceConfigInfo defaultConfig;
    /**
     *  解析配置
     */
    public void parseConfigInfo(Environment env) {
        Map<String, Map<String, Object>> map = parseConfigMapper(env);
        mapper = new HashMap<String, ShardDataSourceConfigInfo>();

        if (map.containsKey(Namespace.SHARD_DATASOURCE_DEFAULT)) {
            defaultConfig = parse(Namespace.SHARD_DATASOURCE_DEFAULT, map.get(Namespace.SHARD_DATASOURCE_DEFAULT));
            mapper.put(Namespace.SHARD_DATASOURCE_DEFAULT, defaultConfig);
        }

        map.forEach((k,v) -> {
            if (k.equals(Namespace.SHARD_DATASOURCE_DEFAULT)) {return;}

            if (defaultConfig != null) {
                mapper.put(k, parse(k, v, defaultConfig));
            } else {
                mapper.put(k, parse(k, v));
            }
        });
    }


    /**
     *  取配置信息
     */
    public ShardDataSourceConfigInfo getConfigInfo(String key) {return mapper.get(key);}
    /**
     *  取全部配置信息
     */
    public Map<String, ShardDataSourceConfigInfo> allConfig() {
        return mapper;
    }


    /**
     *  解析配置信息
     */
    public Map<String, Map<String, Object>> parseConfigMapper(Environment env) {
        ConfigPrefixParser parser = new ConfigPrefixParser(env, Namespace.SHARD_DATASOURCE_PREFIX);
        return parser.allKeys();
    }
    /**
     *  解析配置信息
     */
    public Map<String, ShardDataSourceConfigInfo> parse(Map<String, Map<String, Object>> configMap) {
        Map<String, ShardDataSourceConfigInfo> map = new HashMap<>();
        configMap.forEach((k,v) -> {
            map.put(k, parse(k, v));
        });
        return map;
    }
    public ShardDataSourceConfigInfo parse(String name, Map<String, Object> kvs) {
        String prefix = String.format("%s.%s", Namespace.SHARD_DATASOURCE_PREFIX, name);

        ShardDataSourceConfigInfo info = new ShardDataSourceConfigInfo();
        info.setType(parse(kvs, prefix, String.format("pool.%s", "type"), "druid" ,Object::toString));
        info.setMapperLocations(parse(kvs, prefix, "mapperLocations", Object::toString));
        info.setDriverClassName(parse(kvs, prefix, "driverClassName", Object::toString));
        info.setUrl(parse(kvs, prefix, "url", Object::toString));
        info.setUsername(parse(kvs, prefix, "username", null, Object::toString));
        info.setPassword(parse(kvs, prefix, "password", null, Object::toString));
        info.setPool(parsePool(kvs, prefix));
        info.setDiversion(parseDiversion(kvs, prefix));
        return info;
    }
    public ShardDataSourceConfigInfo parse(String name, Map<String, Object> kvs, ShardDataSourceConfigInfo defaultValue) {
        String prefix = String.format("%s.%s", Namespace.SHARD_DATASOURCE_PREFIX, name);

        ShardDataSourceConfigInfo info = new ShardDataSourceConfigInfo();
        info.setType(parse(kvs, prefix, String.format("pool.%s", "type"), defaultValue.getType() ,Object::toString));
        info.setMapperLocations(parse(kvs, prefix, "mapperLocations", defaultValue.getMapperLocations(), Object::toString));
        info.setDriverClassName(parse(kvs, prefix, "driverClassName", defaultValue.getDriverClassName(), Object::toString));
        info.setUrl(parse(kvs, prefix, "url", defaultValue.getUrl(), Object::toString));
        info.setUsername(parse(kvs, prefix, "username", defaultValue.getUsername(), Object::toString));
        info.setPassword(parse(kvs, prefix, "password", defaultValue.getPassword(), Object::toString));
        info.setPool(parsePool(kvs, prefix, defaultValue.getPool()));
        info.setDiversion(parseDiversion(kvs, prefix, defaultValue.getDiversion()));
        return info;
    }

    protected <T> T parse(Map<String, Object> kvs, String prefix, String key, Function<Object, T> fun) {
        Object v = kvs.get(String.format("%s.%s", prefix, key));
        try {
            return fun.apply(v);
        } catch (RuntimeException e) {
//            log.error(String.format("[ShardDataSourceConfigInfoParser parse] 解析参数有误. key:%s", key), e);
            throw e;
        }
    }
    protected <T> T parse(Map<String, Object> kvs, String prefix, String key, T defaultValue, Function<Object, T> fun) {
        Object v = kvs.get(String.format("%s.%s", prefix, key));
        try {
            return fun.apply(v);
        } catch (Exception e) {
//            log.warn(String.format("[ShardDataSourceConfigInfoParser parse] 解析参数有误. key:%s 给默认值:%s", key, defaultValue));
            return defaultValue;
        }
    }


    /**
     * 解析连接池配置
     */
    protected ShardDataSourceConfigInfo.PoolConfig parsePool(Map<String, Object> kvs, String prefix) {
        ShardDataSourceConfigInfo.PoolConfig pinfo = new ShardDataSourceConfigInfo.PoolConfig();
        pinfo.setInitialSize(parse(kvs, prefix, String.format("pool.%s", "initialSize"), 1, v -> Integer.parseInt(v.toString())));
        pinfo.setMinIdle(parse(kvs, prefix, String.format("pool.%s", "minIdle"), 1, v -> Integer.parseInt(v.toString())));
        pinfo.setMaxActive(parse(kvs, prefix, String.format("pool.%s", "maxActive"), 10, v -> Integer.parseInt(v.toString())));
        pinfo.setMaxWait(parse(kvs, prefix, String.format("pool.%s", "maxWait"), 60000l, v -> Long.parseLong(v.toString())));
        pinfo.setTimeBetweenEevictionRunsMillis(parse(kvs, prefix, String.format("pool.%s", "timeBetweenEevictionRunsMillis"), 60000l, v -> Long.parseLong(v.toString())));
        pinfo.setMinEvictableIdleTimeMillis(parse(kvs, prefix, String.format("pool.%s", "minEvictableIdleTimeMillis"), 30000l, v -> Long.parseLong(v.toString())));
        pinfo.setValidationQuery(parse(kvs, prefix, String.format("pool.%s", "validationQuery"), "select 1", Object::toString));
        pinfo.setTestWhileIdle(parse(kvs, prefix, String.format("pool.%s", "testWhileIdle"), true, v -> Boolean.valueOf(v.toString())));
        pinfo.setTestOnBorrow(parse(kvs, prefix, String.format("pool.%s", "testOnBorrow"), true, v -> Boolean.valueOf(v.toString())));
        pinfo.setTestOnReturn(parse(kvs, prefix, String.format("pool.%s", "testOnReturn"), false, v -> Boolean.valueOf(v.toString())));
        //	是否缓存preparedStatement，也就是PSCache  官方建议MySQL下建议关闭   个人建议如果想用SQL防火墙 建议打开
        pinfo.setPoolPreparedStatements(parse(kvs, prefix, String.format("pool.%s", "poolPreparedStatements"), false, v -> Boolean.valueOf(v.toString())));
        pinfo.setMaxPoolPreparedStatementPerConnectionSize(parse(kvs, prefix, String.format("pool.%s", "maxPoolPreparedStatementPerConnectionSize"), 20, v -> Integer.parseInt(v.toString())));
        return pinfo;
    }
    /**
     * 解析连接池配置（含默认）
     */
    protected ShardDataSourceConfigInfo.PoolConfig parsePool(Map<String, Object> kvs, String prefix, ShardDataSourceConfigInfo.PoolConfig defaultValue) {
        ShardDataSourceConfigInfo.PoolConfig pinfo = new ShardDataSourceConfigInfo.PoolConfig();
        pinfo.setInitialSize(parse(kvs, prefix, String.format("pool.%s", "initialSize"), defaultValue.getInitialSize(), v -> Integer.parseInt(v.toString())));
        pinfo.setMinIdle(parse(kvs, prefix, String.format("pool.%s", "minIdle"), defaultValue.getMinIdle(), v -> Integer.parseInt(v.toString())));
        pinfo.setMaxActive(parse(kvs, prefix, String.format("pool.%s", "maxActive"), defaultValue.getMaxActive(), v -> Integer.parseInt(v.toString())));
        pinfo.setMaxWait(parse(kvs, prefix, String.format("pool.%s", "maxWait"), defaultValue.getMaxWait(), v -> Long.parseLong(v.toString())));
        pinfo.setTimeBetweenEevictionRunsMillis(parse(kvs, prefix, String.format("pool.%s", "timeBetweenEevictionRunsMillis"), defaultValue.getTimeBetweenEevictionRunsMillis(), v -> Long.parseLong(v.toString())));
        pinfo.setMinEvictableIdleTimeMillis(parse(kvs, prefix, String.format("pool.%s", "minEvictableIdleTimeMillis"), defaultValue.getMinEvictableIdleTimeMillis(), v -> Long.parseLong(v.toString())));
        pinfo.setValidationQuery(parse(kvs, prefix, String.format("pool.%s", "validationQuery"), defaultValue.getValidationQuery(), Object::toString));
        pinfo.setTestWhileIdle(parse(kvs, prefix, String.format("pool.%s", "testWhileIdle"), defaultValue.getTestWhileIdle(), v -> Boolean.valueOf(v.toString())));
        pinfo.setTestOnBorrow(parse(kvs, prefix, String.format("pool.%s", "testOnBorrow"), defaultValue.getTestOnBorrow(), v -> Boolean.valueOf(v.toString())));
        pinfo.setTestOnReturn(parse(kvs, prefix, String.format("pool.%s", "testOnReturn"), defaultValue.getTestOnReturn(), v -> Boolean.valueOf(v.toString())));
        //	是否缓存preparedStatement，也就是PSCache  官方建议MySQL下建议关闭   个人建议如果想用SQL防火墙 建议打开
        pinfo.setPoolPreparedStatements(parse(kvs, prefix, String.format("pool.%s", "poolPreparedStatements"), defaultValue.getPoolPreparedStatements(), v -> Boolean.valueOf(v.toString())));
        pinfo.setMaxPoolPreparedStatementPerConnectionSize(parse(kvs, prefix, String.format("pool.%s", "maxPoolPreparedStatementPerConnectionSize"), defaultValue.getMaxPoolPreparedStatementPerConnectionSize(), v -> Integer.parseInt(v.toString())));
        return pinfo;
    }


    /**
     * 解析分流池
     */
    protected ShardDataSourceConfigInfo.DiversionConfig parseDiversion(Map<String, Object> kvs, String prefix) {
        ShardDataSourceConfigInfo.DiversionConfig d = new ShardDataSourceConfigInfo.DiversionConfig();
        d.setEnabled(parse(kvs, prefix, "diversion.enabled", v -> Boolean.valueOf(v.toString())));
        d.setSize(parse(kvs, prefix, "diversion.size", v -> Integer.parseInt(v.toString())));
        return d;
    }
    /**
     * 解析分流池（含默认）
     */
    protected ShardDataSourceConfigInfo.DiversionConfig parseDiversion(Map<String, Object> kvs, String prefix, ShardDataSourceConfigInfo.DiversionConfig defaultValue) {
        ShardDataSourceConfigInfo.DiversionConfig d = new ShardDataSourceConfigInfo.DiversionConfig();
        d.setEnabled(parse(kvs, prefix, "diversion.enabled", defaultValue.isEnabled(), v -> Boolean.valueOf(v.toString())));
        d.setSize(parse(kvs, prefix, "diversion.size", defaultValue.getSize(), v -> Integer.parseInt(v.toString())));
        return d;
    }


    public Map<String, ShardDataSourceConfigInfo> getMapper() {
        return mapper;
    }

    public void setMapper(Map<String, ShardDataSourceConfigInfo> mapper) {
        this.mapper = mapper;
    }
}
